```html
<!DOCTYPE html>
<html lang="zh-CN" class="scroll-smooth">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>从无感认证到完美用户体验的JWT实践 | 技术小馆</title>
    <link href="https://cdn.staticfile.org/font-awesome/6.4.0/css/all.min.css" rel="stylesheet">
    <link href="https://cdn.staticfile.org/tailwindcss/2.2.19/tailwind.min.css" rel="stylesheet">
    <link href="https://fonts.googleapis.com/css2?family=Noto+Serif+SC:wght@400;500;600;700&family=Noto+Sans+SC:wght@300;400;500;700&display=swap" rel="stylesheet">
    <script src="https://cdn.jsdelivr.net/npm/mermaid@latest/dist/mermaid.min.js"></script>
    <style>
        body {
            font-family: 'Noto Sans SC', Tahoma, Arial, Roboto, "Droid Sans", "Helvetica Neue", "Droid Sans Fallback", "Heiti SC", "Hiragino Sans GB", Simsun, sans-serif;
            color: #333;
            line-height: 1.7;
        }
        .serif {
            font-family: 'Noto Serif SC', serif;
        }
        .hero-gradient {
            background: linear-gradient(135deg, #6e8efb 0%, #a777e3 100%);
        }
        .code-block {
            background-color: #282c34;
            border-radius: 8px;
            font-family: 'Courier New', Courier, monospace;
        }
        .hover-scale {
            transition: transform 0.3s ease;
        }
        .hover-scale:hover {
            transform: translateY(-3px);
        }
        .table-container {
            overflow-x: auto;
            -webkit-overflow-scrolling: touch;
        }
        table {
            min-width: 600px;
        }
        .mermaid {
            background-color: white;
            padding: 20px;
            border-radius: 8px;
            margin: 20px 0;
        }
    </style>
</head>
<body class="bg-gray-50">
    <!-- Hero Section -->
    <div class="hero-gradient text-white py-20 px-4 sm:px-6 lg:px-8">
        <div class="max-w-4xl mx-auto text-center">
            <div class="inline-block px-3 py-1 mb-4 text-sm font-medium rounded-full bg-white bg-opacity-20">
                <i class="fas fa-lock mr-2"></i>认证与安全
            </div>
            <h1 class="text-4xl md:text-5xl font-bold mb-6 serif">从无感认证到完美用户体验的JWT实践</h1>
            <p class="text-xl md:text-2xl opacity-90 mb-8 max-w-3xl mx-auto">
                探索双Token认证方案如何平衡安全性与用户体验，实现无缝刷新机制
            </p>
            <div class="flex flex-wrap justify-center gap-4">
                <a href="#introduction" class="px-6 py-3 bg-white text-indigo-700 rounded-lg font-medium hover:bg-opacity-90 transition">
                    <i class="fas fa-book-open mr-2"></i>开始阅读
                </a>
                <a href="#implementation" class="px-6 py-3 border-2 border-white border-opacity-30 text-white rounded-lg font-medium hover:bg-white hover:bg-opacity-10 transition">
                    <i class="fas fa-code mr-2"></i>代码实现
                </a>
            </div>
        </div>
    </div>

    <!-- Main Content -->
    <div class="max-w-4xl mx-auto px-4 sm:px-6 lg:px-8 py-12">
        <!-- Introduction -->
        <section id="introduction" class="mb-16">
            <div class="flex items-center mb-6">
                <div class="h-px bg-gray-300 flex-1"></div>
                <h2 class="px-4 text-2xl font-bold text-gray-800 serif">内容导读</h2>
                <div class="h-px bg-gray-300 flex-1"></div>
            </div>
            
            <div class="bg-white rounded-xl shadow-md overflow-hidden hover-scale transition-all duration-300 mb-8">
                <div class="p-6 sm:p-8">
                    <p class="text-lg text-gray-700 mb-6">最近研究认证方案时发现了个有趣的问题 - 用短Token安全性好但老是让用户重登陆烦死人，用长Token用户体验好但又怕被盗用。这两者真的不能兼得吗？</p>
                    
                    <div class="my-8">
                        <img src="https://cdn.nlark.com/yuque/0/2025/png/21449790/1745809784397-f51edce1-9c44-4608-8787-3c31b906029f.png" alt="双Token认证流程" class="w-full rounded-lg shadow-md">
                    </div>
                    
                    <p class="text-lg text-gray-700">
                        在技术小馆新发的《双Token进化论》里，我整理了个还挺巧妙的方案 - 用短期AccessToken和长期RefreshToken配合使用，有点像门禁卡+家门钥匙的组合，好像能在安全和体验间找到平衡点。不过说实话，试着在项目里用了后，发现在微服务架构下实现起来还是有点复杂的，有时候觉得是不是有点小题大做了。文章里我把实现思路、代码示例和一些踩过的坑都记录下来了，还有几个优化小技巧挺管用的。
                    </p>
                </div>
            </div>
            
            <div class="bg-blue-50 border-l-4 border-blue-500 p-4 rounded-r-lg mb-8">
                <div class="flex">
                    <div class="flex-shrink-0 text-blue-500">
                        <i class="fas fa-question-circle text-xl"></i>
                    </div>
                    <div class="ml-3">
                        <p class="text-blue-700 font-medium">思考讨论</p>
                        <p class="text-blue-600 mt-1">你们平时都用什么认证方案啊？觉得双Token真有必要吗？还是感觉传统方式就够用了？</p>
                    </div>
                </div>
            </div>
        </section>

        <!-- Section 1 -->
        <section id="section1" class="mb-16">
            <div class="flex items-center mb-6">
                <div class="h-px bg-gray-300 flex-1"></div>
                <h2 class="px-4 text-2xl font-bold text-gray-800 serif">一、为什么传统Token方案不够好？</h2>
                <div class="h-px bg-gray-300 flex-1"></div>
            </div>
            
            <div class="mb-8">
                <h3 class="text-xl font-semibold text-gray-800 mb-4 flex items-center">
                    <span class="bg-indigo-100 text-indigo-800 w-8 h-8 rounded-full flex items-center justify-center mr-3">1.1</span>
                    单Token的安全性与体验之争
                </h3>
                <div class="bg-white p-6 rounded-lg shadow-sm border border-gray-100">
                    <p class="text-gray-700 mb-4">传统的单Token认证系统就像一把双刃剑，我们似乎永远无法同时获得安全性和用户体验两大好处：</p>
                    <div class="grid md:grid-cols-2 gap-6 mt-6">
                        <div class="bg-red-50 p-4 rounded-lg border border-red-100">
                            <div class="flex items-center mb-2">
                                <div class="bg-red-100 p-2 rounded-full mr-3">
                                    <i class="fas fa-shield-alt text-red-500"></i>
                                </div>
                                <h4 class="font-semibold text-red-800">短期Token</h4>
                            </div>
                            <p class="text-red-700">设置较短的过期时间（如30分钟），虽然即使被盗用，风险也有限，但用户可能正在专注工作时突然被踢出系统，需要重新登录，用户体验极差。</p>
                        </div>
                        <div class="bg-green-50 p-4 rounded-lg border border-green-100">
                            <div class="flex items-center mb-2">
                                <div class="bg-green-100 p-2 rounded-full mr-3">
                                    <i class="fas fa-user-check text-green-500"></i>
                                </div>
                                <h4 class="font-semibold text-green-800">长期Token</h4>
                            </div>
                            <p class="text-green-700">有效期长（如7天甚至30天），用户体验大幅提升，但一旦Token被盗，攻击者几乎可以长期冒充用户。</p>
                        </div>
                    </div>
                    <p class="text-gray-700 mt-6">这两种方案就像选择"安全还是方便"的哲学问题，没有完美答案，只有权衡利弊。</p>
                </div>
            </div>
            
            <div class="mb-8">
                <h3 class="text-xl font-semibold text-gray-800 mb-4 flex items-center">
                    <span class="bg-indigo-100 text-indigo-800 w-8 h-8 rounded-full flex items-center justify-center mr-3">1.2</span>
                    传统认证方案的痛点分析
                </h3>
                <div class="bg-white p-6 rounded-lg shadow-sm border border-gray-100">
                    <p class="text-gray-700 mb-4">现有的单Token认证方案面临三大痛点：</p>
                    <div class="space-y-4">
                        <div class="flex">
                            <div class="flex-shrink-0 mt-1">
                                <div class="w-6 h-6 rounded-full bg-indigo-100 text-indigo-800 flex items-center justify-center">
                                    <span class="text-sm font-bold">1</span>
                                </div>
                            </div>
                            <div class="ml-3">
                                <p class="text-gray-700 font-medium">安全与体验的矛盾</p>
                                <p class="text-gray-600 mt-1">如前所述，这是最核心的问题。</p>
                            </div>
                        </div>
                        <div class="flex">
                            <div class="flex-shrink-0 mt-1">
                                <div class="w-6 h-6 rounded-full bg-indigo-100 text-indigo-800 flex items-center justify-center">
                                    <span class="text-sm font-bold">2</span>
                                </div>
                            </div>
                            <div class="ml-3">
                                <p class="text-gray-700 font-medium">状态管理复杂</p>
                                <p class="text-gray-600 mt-1">服务端如何知道某个Token已经失效？是记录黑名单还是完全无状态？</p>
                            </div>
                        </div>
                        <div class="flex">
                            <div class="flex-shrink-0 mt-1">
                                <div class="w-6 h-6 rounded-full bg-indigo-100 text-indigo-800 flex items-center justify-center">
                                    <span class="text-sm font-bold">3</span>
                                </div>
                            </div>
                            <div class="ml-3">
                                <p class="text-gray-700 font-medium">跨端一致性难题</p>
                                <p class="text-gray-600 mt-1">Web端、移动端、小程序等不同环境存储Token的方式各异，实现统一的认证体系非常复杂。</p>
                            </div>
                        </div>
                    </div>
                    <p class="text-gray-700 mt-6">特别是在微服务架构下，每个服务可能都需要验证Token，使问题更加复杂。</p>
                </div>
            </div>
            
            <div>
                <h3 class="text-xl font-semibold text-gray-800 mb-4 flex items-center">
                    <span class="bg-indigo-100 text-indigo-800 w-8 h-8 rounded-full flex items-center justify-center mr-3">1.3</span>
                    市面上常见解决方案的对比
                </h3>
                <div class="bg-white p-6 rounded-lg shadow-sm border border-gray-100">
                    <p class="text-gray-700 mb-6">让我们对比几种常见的认证方案：</p>
                    
                    <div class="table-container">
                        <table class="min-w-full divide-y divide-gray-200">
                            <thead class="bg-gray-50">
                                <tr>
                                    <th scope="col" class="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">认证方案</th>
                                    <th scope="col" class="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">优势</th>
                                    <th scope="col" class="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">劣势</th>
                                </tr>
                            </thead>
                            <tbody class="bg-white divide-y divide-gray-200">
                                <tr class="hover:bg-gray-50">
                                    <td class="px-6 py-4 whitespace-nowrap text-sm font-medium text-gray-900">Session-Cookie</td>
                                    <td class="px-6 py-4 text-sm text-gray-500">成熟稳定，易于实现</td>
                                    <td class="px-6 py-4 text-sm text-gray-500">服务端存储压力大，集群同步困难，跨域问题</td>
                                </tr>
                                <tr class="hover:bg-gray-50">
                                    <td class="px-6 py-4 whitespace-nowrap text-sm font-medium text-gray-900">单Token(JWT)</td>
                                    <td class="px-6 py-4 text-sm text-gray-500">无状态，易于分布式扩展</td>
                                    <td class="px-6 py-4 text-sm text-gray-500">安全与体验难以平衡，撤销困难</td>
                                </tr>
                                <tr class="hover:bg-gray-50">
                                    <td class="px-6 py-4 whitespace-nowrap text-sm font-medium text-gray-900">OAuth 2.0</td>
                                    <td class="px-6 py-4 text-sm text-gray-500">标准化，适合第三方授权</td>
                                    <td class="px-6 py-4 text-sm text-gray-500">实现复杂，过度设计</td>
                                </tr>
                                <tr class="hover:bg-gray-50">
                                    <td class="px-6 py-4 whitespace-nowrap text-sm font-medium text-gray-900">单点登录(SSO)</td>
                                    <td class="px-6 py-4 text-sm text-gray-500">多系统一次认证</td>
                                    <td class="px-6 py-4 text-sm text-gray-500">需要中心化认证服务，配置复杂</td>
                                </tr>
                            </tbody>
                        </table>
                    </div>
                    
                    <p class="text-gray-700 mt-6">这些方案各有所长，但在安全性与用户体验的平衡上，都难以做到尽善尽美。</p>
                </div>
            </div>
        </section>

        <!-- Section 2 -->
        <section id="section2" class="mb-16">
            <div class="flex items-center mb-6">
                <div class="h-px bg-gray-300 flex-1"></div>
                <h2 class="px-4 text-2xl font-bold text-gray-800 serif">二、无感刷新的革新之路</h2>
                <div class="h-px bg-gray-300 flex-1"></div>
            </div>
            
            <div class="mb-8">
                <h3 class="text-xl font-semibold text-gray-800 mb-4 flex items-center">
                    <span class="bg-indigo-100 text-indigo-800 w-8 h-8 rounded-full flex items-center justify-center mr-3">2.1</span>
                    什么是双Token认证？
                </h3>
                <div class="bg-white p-6 rounded-lg shadow-sm border border-gray-100">
                    <p class="text-gray-700 mb-4">双Token认证，顾名思义，就是同时使用两种不同角色的Token：</p>
                    
                    <div class="grid md:grid-cols-2 gap-6 mt-6">
                        <div class="bg-purple-50 p-4 rounded-lg border border-purple-100">
                            <div class="flex items-center mb-2">
                                <div class="bg-purple-100 p-2 rounded-full mr-3">
                                    <i class="fas fa-bolt text-purple-500"></i>
                                </div>
                                <h4 class="font-semibold text-purple-800">AccessToken</h4>
                            </div>
                            <p class="text-purple-700 mb-2">短期有效（如15分钟），用于实际的API访问认证，作用域广但寿命短。</p>
                            <ul class="text-purple-700 text-sm space-y-1">
                                <li class="flex items-start">
                                    <i class="fas fa-check-circle text-xs mt-1 mr-2 opacity-70"></i>
                                    <span>携带用户身份信息</span>
                                </li>
                                <li class="flex items-start">
                                    <i class="fas fa-check-circle text-xs mt-1 mr-2 opacity-70"></i>
                                    <span>频繁使用，但生命周期短</span>
                                </li>
                            </ul>
                        </div>
                        <div class="bg-indigo-50 p-4 rounded-lg border border-indigo-100">
                            <div class="flex items-center mb-2">
                                <div class="bg-indigo-100 p-2 rounded-full mr-3">
                                    <i class="fas fa-redo text-indigo-500"></i>
                                </div>
                                <h4 class="font-semibold text-indigo-800">RefreshToken</h4>
                            </div>
                            <p class="text-indigo-700 mb-2">长期有效（如7天或更长），但只能用于刷新获取新的AccessToken，作用域窄但寿命长。</p>
                            <ul class="text-indigo-700 text-sm space-y-1">
                                <li class="flex items-start">
                                    <i class="fas fa-check-circle text-xs mt-1 mr-2 opacity-70"></i>
                                    <span>仅用于获取新的AccessToken</span>
                                </li>
                                <li class="flex items-start">
                                    <i class="fas fa-check-circle text-xs mt-1 mr-2 opacity-70"></i>
                                    <span>安全存储，不轻易暴露</span>
                                </li>
                            </ul>
                        </div>
                    </div>
                    
                    <p class="text-gray-700 mt-6">这就像现实生活中的"门禁卡+钥匙"组合，门禁卡权限大但容易挂失重置，钥匙则藏得更深、用途更单一。</p>
                </div>
            </div>
            
            <div class="mb-8">
                <h3 class="text-xl font-semibold text-gray-800 mb-4 flex items-center">
                    <span class="bg-indigo-100 text-indigo-800 w-8 h-8 rounded-full flex items-center justify-center mr-3">2.2</span>
                    AccessToken与RefreshToken的职责划分
                </h3>
                <div class="bg-white p-6 rounded-lg shadow-sm border border-gray-100">
                    <p class="text-gray-700 mb-4">两种Token的职责边界清晰：</p>
                    
                    <div class="mermaid">
                        pie
                            title Token职责划分
                            "AccessToken" : 65
                            "RefreshToken" : 35
                    </div>
                    
                    <div class="grid md:grid-cols-2 gap-6">
                        <div>
                            <h4 class="font-semibold text-gray-800 mb-3 flex items-center">
                                <i class="fas fa-bolt text-blue-500 mr-2"></i>
                                AccessToken职责
                            </h4>
                            <ul class="text-gray-700 space-y-2">
                                <li class="flex items-start">
                                    <span class="inline-flex items-center justify-center h-5 w-5 rounded-full bg-blue-100 text-blue-800 text-xs mr-2">1</span>
                                    <span>携带用户身份信息</span>
                                </li>
                                <li class="flex items-start">
                                    <span class="inline-flex items-center justify-center h-5 w-5 rounded-full bg-blue-100 text-blue-800 text-xs mr-2">2</span>
                                    <span>用于每次API请求的权限验证</span>
                                </li>
                                <li class="flex items-start">
                                    <span class="inline-flex items-center justify-center h-5 w-5 rounded-full bg-blue-100 text-blue-800 text-xs mr-2">3</span>
                                    <span>频繁使用，但生命周期短</span>
                                </li>
                            </ul>
                        </div>
                        <div>
                            <h4 class="font-semibold text-gray-800 mb-3 flex items-center">
                                <i class="fas fa-redo text-indigo-500 mr-2"></i>
                                RefreshToken职责
                            </h4>
                            <ul class="text-gray-700 space-y-2">
                                <li class="flex items-start">
                                    <span class="inline-flex items-center justify-center h-5 w-5 rounded-full bg-indigo-100 text-indigo-800 text-xs mr-2">1</span>
                                    <span>仅用于获取新的AccessToken</span>
                                </li>
                                <li class="flex items-start">
                                    <span class="inline-flex items-center justify-center h-5 w-5 rounded-full bg-indigo-100 text-indigo-800 text-xs mr-2">2</span>
                                    <span>安全存储，不轻易暴露</span>
                                </li>
                                <li class="flex items-start">
                                    <span class="inline-flex items-center justify-center h-5 w-5 rounded-full bg-indigo-100 text-indigo-800 text-xs mr-2">3</span>
                                    <span>使用频率低，但生命周期长</span>
                                </li>
                            </ul>
                        </div>
                    </div>
                    
                    <p class="text-gray-700 mt-6">这种职责分离遵循了最小权限原则，极大提升了安全性。</p>
                </div>
            </div>
            
            <div class="mb-8">
                <h3 class="text-xl font-semibold text-gray-800 mb-4 flex items-center">
                    <span class="bg-indigo-100 text-indigo-800 w-8 h-8 rounded-full flex items-center justify-center mr-3">2.3</span>
                    无感刷新的工作原理
                </h3>
                <div class="bg-white p-6 rounded-lg shadow-sm border border-gray-100">
                    <p class="text-gray-700 mb-6">无感刷新的核心流程如下：</p>
                    
                    <div class="mermaid">
                        sequenceDiagram
                            participant 用户
                            participant 前端
                            participant 认证服务
                            participant 业务API
                            
                            用户->>前端: 首次登录
                            前端->>认证服务: 发送登录请求
                            认证服务->>认证服务: 验证凭证
                            认证服务->>前端: 返回双Token
                            前端->>前端: 安全存储Token
                            
                            loop 正常使用
                                用户->>前端: 发起请求
                                前端->>业务API: 携带AccessToken
                                业务API->>业务API: 验证Token
                                业务API->>前端: 返回业务数据
                            end
                            
                            前端->>前端: 检测到Token过期
                            前端->>认证服务: 使用RefreshToken请求新Token
                            认证服务->>认证服务: 验证RefreshToken
                            认证服务->>前端: 返回新AccessToken
                            前端->>前端: 更新存储
                            前端->>业务API: 重新发起请求(新Token)
                    </div>
                    
                    <p class="text-gray-700 mt-6">整个过程对用户完全透明，就像汽车在行驶中自动加油一样自然。</p>
                </div>
            </div>
            
            <div>
                <h3 class="text-xl font-semibold text-gray-800 mb-4 flex items-center">
                    <span class="bg-indigo-100 text-indigo-800 w-8 h-8 rounded-full flex items-center justify-center mr-3">2.4</span>
                    方案的技术优势
                </h3>
                <div class="bg-white p-6 rounded-lg shadow-sm border border-gray-100">
                    <p class="text-gray-700 mb-6">双Token认证方案的优势在于：</p>
                    
                    <div class="grid md:grid-cols-2 gap-6">
                        <div class="p-4 border border-green-100 rounded-lg bg-green-50">
                            <div class="flex items-center mb-2">
                                <div class="bg-green-100 p-2 rounded-full mr-3">
                                    <i class="fas fa-balance-scale text-green-500"></i>
                                </div>
                                <h4 class="font-semibold text-green-800">平衡安全与体验</h4>
                            </div>
                            <p class="text-green-700">短期AccessToken保障安全，自动刷新机制保障体验</p>
                        </div>
                        <div class="p-4 border border-blue-100 rounded-lg bg-blue-50">
                            <div class="flex items-center mb-2">
                                <div class="bg-blue-100 p-2 rounded-full mr-3">
                                    <i class="fas fa-shield-virus text-blue-500"></i>
                                </div>
                                <h4 class="font-semibold text-blue-800">降低攻击面</h4>
                            </div>
                            <p class="text-blue-700">即使AccessToken被窃取，攻击窗口期也很短</p>
                        </div>
                        <div class="p-4 border border-purple-100 rounded-lg bg-purple-50">
                            <div class="flex items-center mb-2">
                                <div class="bg-purple-100 p-2 rounded-full mr-3">
                                    <i class="fas fa-sync-alt text-purple-500"></i>
                                </div>
                                <h4 class="font-semibold text-purple-800">便于实现Token撤销</h4>
                            </div>
                            <p class="text-purple-700">只需使RefreshToken失效，无需维护大量黑名单</p>
                        </div>
                        <div class="p-4 border border-indigo-100 rounded-lg bg-indigo-50">
                            <div class="flex items-center mb-2">
                                <div class="bg-indigo-100 p-2 rounded-full mr-3">
                                    <i class="fas fa-puzzle-piece text-indigo-500"></i>
                                </div>
                                <h4 class="font-semibold text-indigo-800">与前端架构解耦</h4>
                            </div>
                            <p class="text-indigo-700">适用于各种前端技术栈，实现方式统一</p>
                        </div>
                    </div>
                </div>
            </div>
        </section>

        <!-- Code Implementation -->
        <section id="implementation" class="mb-16">
            <div class="flex items-center mb-6">
                <div class="h-px bg-gray-300 flex-1"></div>
                <h2 class="px-4 text-2xl font-bold text-gray-800 serif">代码实现</h2>
                <div class="h-px bg-gray-300 flex-1"></div>
            </div>
            
            <div class="mb-8">
                <h3 class="text-xl font-semibold text-gray-800 mb-4 flex items-center">
                    <i class="fas fa-code text-blue-500 mr-3"></i>
                    前端自动刷新实现 (TypeScript)
                </h3>
                <div class="code-block overflow-hidden rounded-lg">
                    <pre class="text-gray-300 p-4 overflow-x-auto"><code>class AuthService {
  private accessToken: string | null = null;
  private refreshToken: string | null = null;
  private tokenExpiresAt: number = 0;
  
  // 初始化，从存储中恢复Token
  async initialize() {
    this.accessToken = sessionStorage.getItem('accessToken');
    // RefreshToken可能来自HttpOnly Cookie，这里简化处理
    this.refreshToken = localStorage.getItem('refreshToken');
    
    const expiry = sessionStorage.getItem('tokenExpiresAt');
    this.tokenExpiresAt = expiry ? parseInt(expiry) : 0;
    
    // 如果AccessToken接近过期，预先刷新
    if (this.shouldRefreshToken()) {
      await this.refreshAccessToken();
    }
  }
  
  // 检查是否需要刷新Token（提前1分钟刷新）
  private shouldRefreshToken(): boolean {
    const currentTime = Date.now();
    const bufferTime = 60 * 1000; // 1分钟缓冲时间
    return this.tokenExpiresAt - currentTime < bufferTime;
  }
  
  // 刷新AccessToken
  async refreshAccessToken(): Promise<void> {
    if (!this.refreshToken) {
      throw new Error('No refresh token available');
    }
    
    try {
      const response = await fetch('/api/auth/refresh', {
        method: 'POST',
        headers: {
          'Content-Type': 'application/json'
        },
        body: JSON.stringify({ refreshToken: this.refreshToken }),
        credentials: 'include' // 如果使用Cookie
      });
      
      if (!response.ok) {
        throw new Error('Failed to refresh token');
      }
      
      const data = await response.json();
      
      // 更新AccessToken
      this.accessToken = data.accessToken;
      
      // 解析JWT获取过期时间
      const payload = JSON.parse(atob(data.accessToken.split('.')[1]));
      this.tokenExpiresAt = payload.exp * 1000; // 转换为毫秒
      
      // 保存到sessionStorage
      sessionStorage.setItem('accessToken', this.accessToken);
      sessionStorage.setItem('tokenExpiresAt', this.tokenExpiresAt.toString());
      
    } catch (error) {
      console.error('Token refresh failed:', error);
      // 重定向到登录页
      this.logout();
      window.location.href = '/login';
    }
  }
  
  // 创建请求拦截器
  createAuthInterceptor() {
    return async (config: any) => {
      // 检查Token是否需要刷新
      if (this.shouldRefreshToken()) {
        await this.refreshAccessToken();
      }
      
      // 添加Token到请求头
      if (this.accessToken) {
        config.headers.Authorization = `Bearer ${this.accessToken}`;
      }
      
      return config;
    };
  }
}</code></pre>
                </div>
            </div>
            
            <div>
                <h3 class="text-xl font-semibold text-gray-800 mb-4 flex items-center">
                    <i class="fas fa-server text-green-500 mr-3"></i>
                    服务端Token生成 (Java)
                </h3>
                <div class="code-block overflow-hidden rounded-lg">
                    <pre class="text-gray-300 p-4 overflow-x-auto"><code>public class TokenService {
    
    @Value("${jwt.access.secret}")
    private String accessSecret;
    
    @Value("${jwt.refresh.secret}")
    private String refreshSecret;
    
    public TokenPair generateTokens(User user) {
        // 生成AccessToken (15分钟有效)
        String accessToken = Jwts.builder()
                .setSubject(user.getId().toString())
                .claim("roles", user.getRoles())
                .claim("type", "access")
                .setIssuedAt(new Date())
                .setExpiration(new Date(System.currentTimeMillis() + 15 * 60 * 1000))
                .signWith(SignatureAlgorithm.HS256, accessSecret)
                .compact();
        
        // 生成RefreshToken (7天有效)
        String jti = UUID.randomUUID().toString();
        String refreshToken = Jwts.builder()
                .setSubject(user.getId().toString())
                .claim("type", "refresh")
                .setId(jti)
                .setIssuedAt(new Date())
                .setExpiration(new Date(System.currentTimeMillis() + 7 * 24 * 60 * 60 * 1000))
                .signWith(SignatureAlgorithm.HS256, refreshSecret)
                .compact();
        
        // 记录RefreshToken，用于后续可能的撤销
        saveRefreshTokenToDb(jti, user.getId(), refreshToken);
        
        return new TokenPair(accessToken, refreshToken);
    }
}</code></pre>
                </div>
            </div>
        </section>

        <!-- Conclusion -->
        <section class="mb-16">
            <div class="bg-gradient-to-r from-blue-50 to-indigo-50 p-8 rounded-xl border border-blue-100">
                <div class="max-w-3xl mx-auto text-center">
                    <h3 class="text-2xl font-bold text-gray-800 mb-4 serif">双Token认证：安全与体验的完美平衡</h3>
                    <p class="text-gray-700 mb-6">通过双Token认证方案，我们成功解决了传统认证系统中安全性与用户体验的矛盾。AccessToken与RefreshToken的职责分离不仅提升了安全性，还实现了无感刷新的流畅体验。无论是在单页应用、移动应用还是复杂的微服务架构中，这一方案都能提供稳定可靠的认证机制。</p>
                    <div class="flex flex-wrap justify-center gap-4">
                        <a href="#" class="px-6 py-3 bg-indigo-600 text-white rounded-lg font-medium hover:bg-indigo-700 transition">
                            <i class="fas fa-book mr-2"></i>阅读完整文章
                        </a>
                        <a href="#" class="px-6 py-3 border-2 border-indigo-600 text-indigo-600 rounded-lg font-medium hover:bg-indigo-50 transition">
                            <i class="fas fa-comments mr-2"></i>参与讨论
                        </a>
                    </div>
                </div>
            </div>
        </section>
    </div>

    <!-- Footer -->
    <footer class="bg-gray-900 text-gray-300 py-8 px-4 sm:px-6 lg:px-8">
        <div class="max-w-4xl mx-auto">
            <div class="flex flex-col md:flex-row justify-between items-center">
                <div class="mb-4 md:mb-0">
                    <h3 class="text-xl font-semibold text-white mb-2">技术小馆</h3>
                    <p class="text-gray-400">探索技术之美，分享开发经验</p>
                </div>
                <div>
                    <a href="http://www.yuque.com/jtostring" class="text-indigo-400 hover:text-indigo-300 transition">
                        <i class="fas fa-globe mr-2"></i>http://www.yuque.com/jtostring
                    </a>
                </div>
            </div>
            <div class="border-t border-gray-800 mt-8 pt-8 text-center text-sm text-gray-500">
                &copy; 2023 技术小馆. 保留所有权利.
            </div>
        </div>
    </footer>

    <script>
        mermaid.initialize({
            startOnLoad: true,
            theme: 'default',
            flowchart: {
                useMaxWidth: false,
                htmlLabels: true
            }
        });
        
        // 平滑滚动
        document.querySelectorAll('a[href^="#"]').forEach(anchor => {
            anchor.addEventListener('click', function (e) {
                e.preventDefault();
                document.querySelector(this.getAttribute('href')).scrollIntoView({
                    behavior: 'smooth'
                });
            });
        });
    </script>
</body>
</html>
```